/* app mqtt

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"

#include "cJSON.h"

#include "esp_log.h"
#include "mqtt_client.h"

#include "app_main.h"
#include "storage.h"


static const char *TAG = "app_mqtt";

static const char *device_type = "switch";

esp_mqtt_client_handle_t dev_client = NULL;
esp_mqtt_client_handle_t client = NULL;

static char ClientID[7+13];
static char strId[15];

static uint8_t OneLevelTopicLen = 15;

static bool DeviceLogin( void );
static int MqttSubMsgSetHdl( char *data);
static int MqttSubMsgGetHdl( char *data);
static int MqttSubMsgUpgradeHdl( char *data);


static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    // your_context_t *context = event->context;
    switch (event->event_id) {
        case MQTT_EVENT_CONNECTED:
            ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
 //           msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);
 //           ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
 
 			xEventGroupSetBits(s_device_event_group, MQTT_CONNECTED_BIT);

 			dev_client = event->client;
 
			char topic[50];

			sprintf(topic, "/%s/switch/set", strId );
			
            msg_id = esp_mqtt_client_subscribe(client, topic, 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

			sprintf(topic, "/%s/switch/get", strId );
			
            msg_id = esp_mqtt_client_subscribe(client, topic, 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);	

			sprintf(topic, "/%s/switch/upgrade", strId );
			
            msg_id = esp_mqtt_client_subscribe(client, topic, 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);				

			DeviceLogin();

			MqttSubMsgGetHdl( event->data );

            break;
        case MQTT_EVENT_DISCONNECTED:
			dev_client = NULL;
            ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
			xEventGroupClearBits(s_device_event_group, MQTT_CONNECTED_BIT);
            break;

        case MQTT_EVENT_SUBSCRIBED:
            ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
//            msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
//            ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
            break;
        case MQTT_EVENT_UNSUBSCRIBED:
            ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_PUBLISHED:
            ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
            break;
        case MQTT_EVENT_DATA:
            ESP_LOGI(TAG, "MQTT_EVENT_DATA");
            printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
            printf("DATA=%.*s\r\n", event->data_len, event->data);

			  /* Decode topic string into a user defined reference */
			char topic2[51];

			if( (event->topic_len > OneLevelTopicLen) && ((event->topic_len - OneLevelTopicLen) < 51) )
			{
				strncpy(topic2, (char *)&event->topic[ OneLevelTopicLen ], event->topic_len - OneLevelTopicLen );

				topic2[event->topic_len - OneLevelTopicLen] = '\0';
			}

			printf("topic2:%s\r\n", topic2 );

			int errCode, topicType;

			topicType = 0;
			
			if( strcmp( topic2, "/switch/set") == 0 )  
			{
				errCode = MqttSubMsgSetHdl( event->data );

				topicType = 1;
			}
			else if( strcmp( topic2, "/switch/get") == 0 )
			{
				MqttSubMsgGetHdl( event->data );
			}
			else if( strcmp( topic2, "/switch/upgrade") == 0 )
			{
				errCode = MqttSubMsgUpgradeHdl( event->data );
				topicType = 2;
			}
			

			if( topicType > 0 )
			{
				char topic_ack[50];
				char errCodeStr[10];

				if( topicType == 1 )
				{
					sprintf(topic_ack, "/%s/switch/set_ack", strId );
				}
				else if( topicType == 2 )
				{
					sprintf(topic_ack, "/%s/switch/upgrade_ack", strId );
				}
				
				sprintf(errCodeStr, "%d", errCode );
				
				cJSON* cJson_post = cJSON_CreateObject();
				if( errCode == 200 )
				{
					cJSON_AddStringToObject( cJson_post, "message", "success" );
				}
				else
				{
					cJSON_AddStringToObject( cJson_post, "message", "fail" );
				}
				
				cJSON_AddStringToObject( cJson_post, "code", errCodeStr );
				
				char *json_data = cJSON_Print( cJson_post );
				cJSON_Delete(cJson_post);
				printf("%s\n",json_data);
				
				msg_id = esp_mqtt_client_publish(dev_client, topic_ack, json_data, 0, 1, 0);
				ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
				
				cJSON_free( json_data ); 
			}
		
            break;
        case MQTT_EVENT_ERROR:
            ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
            break;
        default:
            ESP_LOGI(TAG, "Other event id:%d", event->event_id);
            break;
    }
    return ESP_OK;
}

static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
    mqtt_event_handler_cb(event_data);
}


void mqtt_app_start( uint8_t *mac )
{
    esp_mqtt_client_config_t mqtt_cfg = {
//        .uri = CONFIG_BROKER_URL,
			.client_id = ClientID,
			.username = DeviceConfig.mqttUsername,
			.password = DeviceConfig.mqttPassword,
//			.host = "47.92.129.18",			//test server
			.host = DeviceConfig.mqttDomain,			//wzx
			.port = DeviceConfig.mqttPort,
   	};
#if CONFIG_BROKER_URL_FROM_STDIN
    char line[128];

    if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) {
        int count = 0;
        printf("Please enter url of mqtt broker\n");
        while (count < 128) {
            int c = fgetc(stdin);
            if (c == '\n') {
                line[count] = '\0';
                break;
            } else if (c > 0 && c < 127) {
                line[count] = c;
                ++count;
            }
            vTaskDelay(10 / portTICK_PERIOD_MS);
        }
        mqtt_cfg.uri = line;
        printf("Broker url: %s\n", line);
    } else {
        ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
        abort();
    }
#endif /* CONFIG_BROKER_URL_FROM_STDIN */

	sprintf(ClientID,"SWITCH_%02X%02X%02X%02X%02X%02X",mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
	sprintf(strId,"ID%02X%02X%02X%02X%02X%02X",mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
	printf("ClientID:%s\n", ClientID );
	printf("strId:%s\n", strId );

    client = esp_mqtt_client_init(&mqtt_cfg);
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}
/**
  * @brief  None.
  * @param  None.
  * @retval None.
  */
void mqtt_app_Restart( void )
{
	if( client != NULL )
	{
		esp_mqtt_client_destroy( client );
	}

		esp_mqtt_client_config_t mqtt_cfg = {
	//		  .uri = CONFIG_BROKER_URL,
				.client_id = ClientID,
				.username = DeviceConfig.mqttUsername,
				.password = DeviceConfig.mqttPassword,
				.host = DeviceConfig.mqttDomain,
				.port = DeviceConfig.mqttPort,
		};
#if CONFIG_BROKER_URL_FROM_STDIN
		char line[128];
	
		if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) {
			int count = 0;
			printf("Please enter url of mqtt broker\n");
			while (count < 128) {
				int c = fgetc(stdin);
				if (c == '\n') {
					line[count] = '\0';
					break;
				} else if (c > 0 && c < 127) {
					line[count] = c;
					++count;
				}
				vTaskDelay(10 / portTICK_PERIOD_MS);
			}
			mqtt_cfg.uri = line;
			printf("Broker url: %s\n", line);
		} else {
			ESP_LOGE(TAG, "Configuration mismatch: wrong broker url");
			abort();
		}
#endif /* CONFIG_BROKER_URL_FROM_STDIN */

    client = esp_mqtt_client_init(&mqtt_cfg);
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}

/**
  * @brief  None.
  * @param  None.
  * @retval None.
  */
static bool DeviceLogin( void )
{
	int msg_id;
	char topic[50];
	
	sprintf(topic, "/%s/switch/login", strId );
	
	cJSON* cJson_post = cJSON_CreateObject();
	cJSON_AddStringToObject( cJson_post, "type", device_type );
	cJSON_AddStringToObject( cJson_post, "mac", (char *)&strId[2] );
	cJSON_AddStringToObject( cJson_post, "ver", AppVer );
	
	char *json_data = cJSON_Print( cJson_post );
	cJSON_Delete(cJson_post);
	printf("%s\n",json_data);
	
	msg_id = esp_mqtt_client_publish(dev_client, topic, json_data, 0, 1, 0);
	ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
	
	cJSON_free( json_data ); 

	return true;

}

/**
  * @brief  None.
  * @param  None.
  * @retval None.
  */
void RelayStateReport( uint8_t state )
{
	int msg_id;

	char strState[10];

	if( state == 0 )
	{
		strcpy(strState, "off");
	}
	else if( state == 1 )
	{
		strcpy(strState, "on");
	}
	else
	{
		strcpy(strState, "unkow");
	}
	
	char topic[50];
	
	sprintf(topic, "/%s/switch/post", strId );
	
	cJSON* cJson_post = cJSON_CreateObject();
	cJSON_AddStringToObject( cJson_post, "state", strState );
	cJSON_AddStringToObject( cJson_post, "ver", AppVer );
	
	char *json_data = cJSON_Print( cJson_post );
	cJSON_Delete(cJson_post);
	printf("%s\n",json_data);
	
	msg_id = esp_mqtt_client_publish(dev_client, topic, json_data, 0, 1, 0);
	ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
	
	cJSON_free( json_data ); 

}

/**
  * @brief  None.
  * @param  None.
  * @retval None.
  */
void UpgradeResultReport( uint8_t result )
{
	int msg_id;
	char topic[50];
	
	sprintf(topic, "/%s/switch/upgrade_ret", strId );
	
	cJSON* cJson_post = cJSON_CreateObject();
	if( result == ESP_OK )
	{
		cJSON_AddStringToObject( cJson_post, "message", "success" );
	}
	else
	{
		cJSON_AddStringToObject( cJson_post, "message", "fail" );
	}
	
	char *json_data = cJSON_Print( cJson_post );
	cJSON_Delete(cJson_post);
	printf("%s\n",json_data);
	
	msg_id = esp_mqtt_client_publish(dev_client, topic, json_data, 0, 1, 0);
	ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
	
	cJSON_free( json_data ); 

}

/**
  * @brief  MqttSubMsgSetHdl.
  * @param  None.
  * @retval None.
  */
static int MqttSubMsgSetHdl( char *data)
{
	int code = 200;

	cJSON *pJsonRoot = cJSON_Parse( data );

	if( pJsonRoot!= NULL )
	{
		cJSON * pJsonObj = cJSON_GetObjectItem(pJsonRoot, "action");
		if( pJsonObj != NULL )
		{
			if( cJSON_IsString(pJsonObj) )
			{
				printf("action:%s\n", pJsonObj->valuestring );

				if( strcmp( pJsonObj->valuestring, "on") == 0 )
				{
					GreenLedOn();
					RelayGpioOn();
					xEventGroupSetBits( Relay_check_event_group, 0x01 );
				}
				else if( strcmp( pJsonObj->valuestring, "off") == 0 )
				{
					GreenLedOff();
					RelayGpioOff();

					xEventGroupSetBits( Relay_check_event_group, 0x01 );
				}
				else if( strcmp( pJsonObj->valuestring, "on-off") == 0  )
				{
					cJSON * pJsonObj_OnOff = cJSON_GetObjectItem(pJsonRoot, "delay");
					
					if( pJsonObj_OnOff != NULL )
					{
						if( cJSON_IsNumber( pJsonObj_OnOff ) )
						{
							printf("delay:%ds", pJsonObj_OnOff->valueint );

							if( pJsonObj_OnOff->valueint > 0 )
							{
								if( xTimerIsTimerActive( Timer1 ) != pdFALSE ) 
								{
									xTimerStop( Timer1, 20 );
								}
							
								if( xTimerChangePeriod( Timer1, pJsonObj_OnOff->valueint / portTICK_PERIOD_MS, 10 ) == pdPASS )
								{
									if( xTimerStart( Timer1, 20 ) == pdTRUE )
									{
										GreenLedOn();						//green Led on.
										RelayGpioOn();	
										xEventGroupSetBits( Relay_check_event_group, 0x01 );							
									}
									else
									{
										code = 501;
									}
								}	
								else
								{
									code = 501;
								}
							}
							else
							{
								code = 406;
							}
						}
						else
						{
							code = 404;
						}
					}
					else
					{
						code = 403;
					}

				}
				else
				{
					code = 405;
				}
			}
			else
			{
				code = 404;
			}
			
		}
		else
		{
			printf("cJSON_GetObjectItem fail\n");
			code = 403;
		}

		cJSON_Delete( pJsonRoot );
	}
	else
	{
		printf("cJSON_Parse fail\n");
		
		code = 402;
	}

	return code;
}


/**
  * @brief  MqttSubMsgGetHdl.
  * @param  None.
  * @retval None.
  */
static int MqttSubMsgGetHdl( char *data)
{
	xEventGroupSetBits( Relay_check_event_group, 0x01 );

	return 0;
}

/**
  * @brief  MqttSubMsgGetHdl.
  * @param  None.
  * @retval None.
  */
static int MqttSubMsgUpgradeHdl( char *data)
{
	int code = 200;

	cJSON *pJsonRoot = cJSON_Parse( data );

	if( pJsonRoot!= NULL )
	{
		cJSON * pJsonObj = cJSON_GetObjectItem(pJsonRoot, "url");
		if( pJsonObj != NULL )
		{
			if( cJSON_IsString(pJsonObj) )
			{
				printf("url:%s\n", pJsonObj->valuestring );

				if( xTaskCreate(ota_task, "ota_task", 8192, pJsonObj->valuestring, 5, NULL) != pdPASS )
				{
					code = 502;
				}
			}
			else
			{
				code = 404;
			}
		}
		else
		{
			printf("cJSON_GetObjectItem fail\n");
			code = 403;
		}
	}
	else
	{
		printf("cJSON_Parse fail\n");
		
		code = 402;

	}

	return code;
}


